/*---------------------------------------------------------------------------*
 * Copyright (C) 2012 Ryan M. Murphy <ryan.m.murphy.77@gmail.com>
 *
 * File Name   : usr_hmi.c
 *
 * Project     : Watts-rock
 *               <http://code.google.com/p/watts-rock-motherboard/>
 *
 * Description : This file is an extension of usr_bms.c, and is responsible for
 *               providing status and power down control via an LCD and push
 *               buttons.
 *
 * Last Update : Nov, 8, 2012
 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*
 * Include Files
 *---------------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "arb_error.h"
#include "arb_thread.h"
#include "arb_semaphore.h"
#include "arb_device.h"
#include "arb_sysTimer.h"
#include "arb_printf.h"
#include "usr_bms.h"
#include "utl_stateMachine.h"
#include "drv_lcd.h"

/*---------------------------------------------------------------------------*
 * Private Defines
 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*
 * The thread update rate in system timer ticks
 *---------------------------------------------------------------------------*/
#define HMI_UPDATE_RATE (.5) /*Seconds*/
#define HMI_SLEEP_TICKS ((uint16_t)((float)ARB_TICKS_PER_SECOND*\
(float)HMI_UPDATE_RATE))
#define HMI_WELCOME_SCREEN_DURATION (5)  /*Seconds*/
#define HMI_HOME_SCREEN_DURATION    (55) /*Seconds*/
#define HMI_BACKLIGHT_DURTION       (10) /*Seconds*/
#define HMI_BUTTON_PORT             (GPIO_PORTH)
#define HMI_BACKLIGHT_PIN           (PIN_0)
#define HMI_MENU_PIN                (PIN_1)
#define HMI_PWR_PIN                 (PIN_2)
#define HMI_PWR_BUTTON_DURATION     (5)

#if 0
/*---------------------------------------------------------------------------*
 * Private Typedefs
 *---------------------------------------------------------------------------*/
typedef enum
{
   /*------------------------------------------------------------------------*
    * The system has booted and welcome screen is being displayed.
    *------------------------------------------------------------------------*/
   HMI_BOOT = 0,
   /*------------------------------------------------------------------------*
    * The home screen is being displayed.
    *------------------------------------------------------------------------*/
   HMI_HOME,
   /*------------------------------------------------------------------------*
    * The LCD is shut-off
    *------------------------------------------------------------------------*/
   HMI_IDLE,
   /*------------------------------------------------------------------------*
    * A fault has been detected with the system.
    *------------------------------------------------------------------------*/
   HMI_FAULT,
   /*------------------------------------------------------------------------*
    * The battery has gotten very low and the system is about to go into
    * deep sleep.
    *------------------------------------------------------------------------*/
   HMI_DEEP_DISCHARGE,
   /*------------------------------------------------------------------------*
    * This variable is always last!
    *------------------------------------------------------------------------*/
   HMI_NUM_STATES

}t_hmiState;

typedef struct
{
   /*------------------------------------------------------------------------*
    * Resources that can be shared amongst multiple users (either a global
    * buffer or IO device) need to be protected against race conditions. We
    * use this semaphore for just that purpose.
    *------------------------------------------------------------------------*/
   t_SEMHANDLE t_mutex;

   /*------------------------------------------------------------------------*
    * Handle to the console thread
    *------------------------------------------------------------------------*/
   t_THRDHANDLE t_hmiThread;

   /*------------------------------------------------------------------------*
    * Handle to the BMS driver.
    *------------------------------------------------------------------------*/
   t_DEVHANDLE t_lcdHndl;

   /*------------------------------------------------------------------------*
    * Handle to the state-machine object that defines how the BMS operates.
    *------------------------------------------------------------------------*/
   t_STMNHNDL t_stateMn;

   /*------------------------------------------------------------------------*
    * The last known state of the BMS.
    *------------------------------------------------------------------------*/
   t_hmiState t_prevState;

   /*------------------------------------------------------------------------*
    * This variable stores the time at which the HMI first enters a new state.
    *------------------------------------------------------------------------*/
   t_sysTime t_startTime;

   /*------------------------------------------------------------------------*
    * If true, the user wants to turn on the back light.
    *------------------------------------------------------------------------*/
   bool b_bckLghtButtonEn;

   /*------------------------------------------------------------------------*
    * If true, the user wants to cycle through the menus.
    *------------------------------------------------------------------------*/
   bool b_menuButtonEn;

   /*------------------------------------------------------------------------*
    * If true, the user wants to power down the system.
    *------------------------------------------------------------------------*/
   bool b_pwrDownButtonEn;

   /*------------------------------------------------------------------------*
    * If true, the system is about to power down.
    *------------------------------------------------------------------------*/
   bool b_pwrDown;

   /*------------------------------------------------------------------------*
    * The last known cursor position.
    *------------------------------------------------------------------------*/
   t_cursPos t_cPos;

   /*------------------------------------------------------------------------*
    * When the battery gets lower than 12.5%, this variable controls the
    * blinking of the last bar in the fuel gage.
    *------------------------------------------------------------------------*/
   bool b_blinkControl;

   /*------------------------------------------------------------------------*
    * This variable marks the time at which the back light was turned on.
    *------------------------------------------------------------------------*/
   t_sysTime t_bckLghtStartTime;

   /*------------------------------------------------------------------------*
    * This variable marks the time at which the pwr down button was pressed.
    *------------------------------------------------------------------------*/
   t_sysTime t_pwrDwnBtnStartTime;

   /*------------------------------------------------------------------------*
    * Returns the last known battery voltage, current, temperature, fuel gage
    * and BMS state.
    *------------------------------------------------------------------------*/
   t_bmsMeasStatus t_mStatus;

}t_hmiObjct;

/*---------------------------------------------------------------------------*
 * Private Function Prototypes
 *---------------------------------------------------------------------------*/
static void usr_hmi( t_parameters t_param,
                     t_arguments  t_args);
static void usr_hmiHome( int32_t i_stateData,
                         uint8_t c_prevState);
static int8_t usr_mainMemu( void);
static void usr_hmiUpdateBcklghtBtn( void);
static void usr_hmiUpdateMenuBtn( void);
static void usr_hmiUpdatePwrDwnBtn( void);
static void usr_hmiBoot( int32_t i_stateData,
                         uint8_t c_prevState);
static void usr_displayFuelGauge( float *pf_fuel);
static void usr_hmiIdle( int32_t i_stateData,
                         uint8_t c_prevState);
static void usr_hmiFault( int32_t i_stateData,
                          uint8_t c_prevState);
static void usr_hmiDeepDischarge( int32_t i_stateData,
                                  uint8_t c_prevState);
static void usr_hmiStateMachineInit( void);
static void usr_checkForButtonPress( void);
static void usr_clearButtonPress( void);
static bool usr_sendPwrDwnMsgToBms( void);
static bool usr_sendFaultAckToBms( void);

/*---------------------------------------------------------------------------*
 * Private Global Variables
 *---------------------------------------------------------------------------*/
static t_hmiObjct gt_hmiObjct;

/*---------------------------------------------------------------------------*
 * Private Function Definitions
 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*
 * This function controls the duration in which the back light is enabled.
 *---------------------------------------------------------------------------*/
static void usr_hmiUpdateBcklghtBtn( void)
{
   t_sysTime t_currTime;
   int8_t c_elapsedTime;

   /*------------------------------------------------------------------------*
    * Has the user pressed the back light button?
    *------------------------------------------------------------------------*/
   if( utl_isGpioHigh( HMI_BUTTON_PORT, HMI_BACKLIGHT_PIN))
   {

      /*---------------------------------------------------------------------*
       * Record the time this event happened.
       *---------------------------------------------------------------------*/
      if( gt_hmiObjct.b_bckLghtButtonEn == false)
      {
         gt_hmiObjct.b_bckLghtButtonEn = true;
         gt_hmiObjct.t_bckLghtStartTime = arb_sysTimeNow();

         arb_ioctl( gt_hmiObjct.t_lcdHndl,
                    LCD_BACKLIGHT_ON,
                    0);
      }

   }/*End if( gt_hmiObjct.b_bckLghtButtonEn == true)*/
   else
   {
      gt_hmiObjct.b_bckLghtButtonEn = false;
   }

   if( gt_hmiObjct.t_bckLghtStartTime.l_ticks > 0)
   {
      t_currTime = arb_sysTimeNow();
      c_elapsedTime = (int8_t)t_currTime.c_sec -
      (int8_t)gt_hmiObjct.t_bckLghtStartTime.c_sec;
      if( c_elapsedTime < 0)
          c_elapsedTime += 60;

      if( c_elapsedTime >= HMI_BACKLIGHT_DURTION)
      {
         arb_ioctl( gt_hmiObjct.t_lcdHndl,
                    LCD_BACKLIGHT_OFF,
                    0);
         memset( (void *)&gt_hmiObjct.t_bckLghtStartTime, 0, sizeof(
         gt_hmiObjct.t_bckLghtStartTime));

      }/*End if( c_elapsedTime >= HMI_BACKLIGHT_DURTION)*/

   }/*End if( gt_hmiObjct.t_bckLghtStartTime.l_ticks > 0)*/

}/*End usr_hmiUpdateBcklghtBtn*/

/*---------------------------------------------------------------------------*
 * This function updates the
 *---------------------------------------------------------------------------*/
static void usr_hmiUpdateMenuBtn( void)
{

   /*------------------------------------------------------------------------*
    * Has the user pressed the back light button?
    *------------------------------------------------------------------------*/
   if( utl_isGpioHigh( HMI_BUTTON_PORT, HMI_MENU_PIN))
   {
      if( gt_hmiObjct.b_menuButtonEn == false)
      {
         gt_hmiObjct.b_menuButtonEn = true;
      }

   }
   else
   {
      gt_hmiObjct.b_menuButtonEn = false;
   }

}/*End usr_hmiUpdateMenuBtn*/

static void usr_hmiUpdatePwrDwnBtn( void)
{
   t_sysTime t_currTime;
   int8_t c_elapsedTime;
#if 0
   /*------------------------------------------------------------------------*
    * Has the user pressed the power down button?
    *------------------------------------------------------------------------*/
   if( utl_isGpioHigh( HMI_BUTTON_PORT, HMI_PWR_PIN))
   {

      /*---------------------------------------------------------------------*
       * Record the time this event first happened.
       *---------------------------------------------------------------------*/
      if( gt_hmiObjct.b_pwrDownButtonEn == false)
      {
         gt_hmiObjct.b_pwrDownButtonEn = true;
         gt_hmiObjct.t_pwrDwnBtnStartTime = arb_sysTimeNow();
      }

   }
   else
   {
      gt_hmiObjct.b_pwrDownButtonEn = false;
   }

   if( (gt_hmiObjct.t_pwrDwnBtnStartTime.l_ticks > 0) &&
       (gt_hmiObjct.b_pwrDownButtonEn == true))
   {
      t_currTime = arb_sysTimeNow();
      c_elapsedTime = (int8_t)t_currTime.c_sec -
      (int8_t)gt_hmiObjct.t_pwrDwnBtnStartTime.c_sec;
      if( c_elapsedTime < 0)
          c_elapsedTime += 60;

      if( c_elapsedTime >= HMI_PWR_BUTTON_DURATION)
      {
         gt_hmiObjct.b_pwrDown = true;

      }/*End if( c_elapsedTime >= HMI_PWR_BUTTON_DURATION)*/

   }/*End if( gt_hmiObjct.t_pwrDwnBtnStartTime.l_ticks > 0)*/
#endif
}/*End usr_hmiUpdatePwrDwnBtn*/

static void usr_checkForButtonPress( void)
{
   usr_hmiUpdateMenuBtn();
   usr_hmiUpdateBcklghtBtn();

}/*End usr_checkForButtonPress*/

static void usr_clearButtonPress( void)
{
   gt_hmiObjct.b_menuButtonEn    = false;
   gt_hmiObjct.b_bckLghtButtonEn = false;

}/*End usr_clearButtonPress*/

static void usr_hmiBoot( int32_t i_stateData,
                         uint8_t c_prevState)
{
   t_cursPos t_cur;
   uint8_t ac_buff[50];
   uint16_t s_size;
   t_sysTime t_currTime;
   int8_t c_elapsedTime;
   t_stmnError t_stErr;
   static bool b_dirRight;

   if( c_prevState != HMI_BOOT)
   {

      gt_hmiObjct.t_prevState = (t_hmiState)c_prevState;
      gt_hmiObjct.t_startTime = arb_sysTimeNow();

      /*---------------------------------------------------------------------*
       * Initialize the LCD module...
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_INIT,
                 0);

      /*---------------------------------------------------------------------*
       * Turn the display on
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_DISPLAY_ON,
                 0);

      /*---------------------------------------------------------------------*
       * Turn the back light off
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      t_cur.c_row = 0;
      t_cur.c_col = 1;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Welcome"));
      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      t_cur.c_row = 1;
      t_cur.c_col = 3;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("to"));
      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 0;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Watts-rock"));
      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      /*---------------------------------------------------------------------*
       * Begin scrolling to the right
       *---------------------------------------------------------------------*/
      b_dirRight = true;
      gt_hmiObjct.t_cPos.c_row = t_cur.c_row;
      gt_hmiObjct.t_cPos.c_col = t_cur.c_col;

      /*---------------------------------------------------------------------*
       * Buttons do not carry over from state-to-state.
       *---------------------------------------------------------------------*/
      gt_hmiObjct.b_menuButtonEn    = false;
      gt_hmiObjct.b_bckLghtButtonEn = false;
      gt_hmiObjct.b_pwrDownButtonEn = false;
      gt_hmiObjct.b_pwrDown         = false;

   }/*End if( c_prevState != HMI_BOOT)*/

   t_currTime = arb_sysTimeNow();
   c_elapsedTime = (int8_t)t_currTime.c_sec - (int8_t)gt_hmiObjct.t_startTime.c_sec;
   if( c_elapsedTime < 0)
      c_elapsedTime += 60;

   if( b_dirRight == true)
   {
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_SCROLL_DISPLAY_RIGHT,
                 0);
   }
   else
   {
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_SCROLL_DISPLAY_LEFT,
                 0);
   }

   gt_hmiObjct.t_cPos.c_col++;
   if( gt_hmiObjct.t_cPos.c_col == 10)
   {
      gt_hmiObjct.t_cPos.c_col = 0;
      b_dirRight = !b_dirRight;
   }

   /*------------------------------------------------------------------------*
    * Is it time to transition to the home screen?
    *------------------------------------------------------------------------*/
   if( c_elapsedTime >= HMI_WELCOME_SCREEN_DURATION)
   {
      t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                     (uint8_t)HMI_HOME,
                                     0);

   }/*End if( c_elapsedTime >= HMI_WELCOME_SCREEN_DURATION)*/

}/*End usr_hmiBoot*/

static void usr_displayFuelGauge( float *pf_fuel)
{
   t_cursPos t_cur;

   if( *pf_fuel > 75)
   {

      t_cur.c_row = 1;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 1;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 16;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
   }
   else if( *pf_fuel > 50)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/

      t_cur.c_row = 1;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 16;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
   }
   else if( *pf_fuel > 25)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 1;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 16;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
   }
   else if( *pf_fuel > 12.5)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 1;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/

      t_cur.c_row = 3;
      t_cur.c_col = 16;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF); /*Black box*/
   }
   else /*Blink the last bar when battery very low...*/
   {
      t_cur.c_row = 1;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 19;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 1;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 18;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/
      t_cur.c_row = 3;
      t_cur.c_col = 17;
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFE); /*Black box*/

      if( gt_hmiObjct.b_blinkControl == false)
      {
         gt_hmiObjct.b_blinkControl = true;
         t_cur.c_row = 3;
         t_cur.c_col = 16;
         arb_ioctl( gt_hmiObjct.t_lcdHndl,
                    LCD_CURSOR_SET_POS,
                    (uint32_t)((uint16_t)&t_cur));
         arb_ioctl( gt_hmiObjct.t_lcdHndl,
                    LCD_WRITE_BUILT_IN_FONT,
                    0xFF); /*Black box*/
      }
      else
      {
         gt_hmiObjct.b_blinkControl = false;
         t_cur.c_row = 3;
         t_cur.c_col = 16;
         arb_ioctl( gt_hmiObjct.t_lcdHndl,
                    LCD_CURSOR_SET_POS,
                    (uint32_t)((uint16_t)&t_cur));
         arb_ioctl( gt_hmiObjct.t_lcdHndl,
                    LCD_WRITE_BUILT_IN_FONT,
                    0xA5); /*Tiny dot*/
      }

   }

}/*End usr_displayFuelGauge*/

static int8_t usr_mainMemu( void)
{
   t_cursPos t_cur;
   uint8_t ac_buff[50];
   uint16_t s_size;
   t_sysTime t_currTime;
   int8_t c_elapsedTime;
   float af_bmsMeas[4];

   /*------------------------------------------------------------------------*
    * Display the banner...
    *------------------------------------------------------------------------*/
   t_cur.c_row = 0;
   t_cur.c_col = 1;

   arb_ioctl( gt_hmiObjct.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("www.watts-rock.com"));
   arb_write( gt_hmiObjct.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);

   t_currTime = arb_sysTimeNow();
   c_elapsedTime = (int8_t)t_currTime.c_sec -
   (int8_t)gt_hmiObjct.t_startTime.c_sec;
   if( c_elapsedTime < 0)
      c_elapsedTime += 60;

   af_bmsMeas[0] = gt_hmiObjct.t_mStatus.t_meas.f_volts;
   af_bmsMeas[1] = gt_hmiObjct.t_mStatus.t_meas.f_current;
   af_bmsMeas[2] = gt_hmiObjct.t_mStatus.t_meas.f_temp;
   af_bmsMeas[3] = gt_hmiObjct.t_mStatus.f_gasGuage;

   /*------------------------------------------------------------------------*
    * Round to the second decimal place...
    *------------------------------------------------------------------------*/
   af_bmsMeas[0] = roundf( af_bmsMeas[0]*100.0f) / 100.0f;
   /*------------------------------------------------------------------------*
    * Round to the third decimal place...
    *------------------------------------------------------------------------*/
   af_bmsMeas[1] = roundf( af_bmsMeas[1]*1000.0f) / 1000.0f;
   /*------------------------------------------------------------------------*
    * Round to the nearest integer and change from Celsius to Fahrenheit
    *------------------------------------------------------------------------*/
   af_bmsMeas[2] = (roundf( ((af_bmsMeas[2])*9.0f/5.0f) + 32.0f));
   /*------------------------------------------------------------------------*
    * Round to the nearest integer
    *------------------------------------------------------------------------*/
   af_bmsMeas[3] = roundf( af_bmsMeas[3]);

   /*------------------------------------------------------------------------*
    * Display the voltage, current, and temperature.
    *------------------------------------------------------------------------*/
   t_cur.c_row = 1;
   t_cur.c_col = 0;

   arb_ioctl( gt_hmiObjct.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("%2.2fV"), af_bmsMeas[0]);

   arb_write( gt_hmiObjct.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);

   t_cur.c_row = 2;
   t_cur.c_col = 0;

   arb_ioctl( gt_hmiObjct.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("%2.3fA"), af_bmsMeas[1]);

   arb_write( gt_hmiObjct.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);

   t_cur.c_row = 3;
   t_cur.c_col = 0;

   arb_ioctl( gt_hmiObjct.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("%3.0fF"), af_bmsMeas[2]);

   arb_write( gt_hmiObjct.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);
#if 0
   /*------------------------------------------------------------------------*
    * Has the charger been connected?
    *------------------------------------------------------------------------*/
   if( gt_hmiObjct.t_mStatus.t_state == BMS_MPP_CHARGE)
   {
      t_cur.c_row = 3;
      t_cur.c_col = 4;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR(" MPP Track"));

      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);
   }
   else if( gt_hmiObjct.t_mStatus.t_state == BMS_CC_CHARGE)
   {
      t_cur.c_row = 3;
      t_cur.c_col = 4;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR(" CC Charge"));

      arb_write( gt_hmiObjct.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);
   }
   else if( gt_hmiObjct.t_mStatus.t_state == BMS_CV_CHARGE)
   {
      t_cur.c_row = 3;
      t_cur.c_col = 4;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR(" CV Charge"));

      arb_write( gt_hmiObjct.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);
   }
   else
   {
      t_cur.c_row = 3;
      t_cur.c_col = 4;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("__________"));

      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);
   }
#endif
   /*------------------------------------------------------------------------*
    * Display the fuel gage.
    *------------------------------------------------------------------------*/
   usr_displayFuelGauge( &af_bmsMeas[3]);

   return c_elapsedTime;

}/*End usr_mainMemu*/

static void usr_hmiHome( int32_t i_stateData,
                         uint8_t c_prevState)
{

   t_stmnError t_stErr;
   int8_t c_elapsedTime;

   if( c_prevState != HMI_HOME)
   {
      gt_hmiObjct.t_startTime = arb_sysTimeNow();
      gt_hmiObjct.t_prevState = (t_hmiState)c_prevState;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_DISPLAY_ON,
                 0);

   }/*End if( c_prevState != HMI_HOME)*/

   /*------------------------------------------------------------------------*
    * Has the user requested power down?
    *------------------------------------------------------------------------*/
   usr_hmiUpdatePwrDwnBtn();

   /*------------------------------------------------------------------------*
    * Has the user pressed a button?
    *------------------------------------------------------------------------*/
   if( (gt_hmiObjct.b_bckLghtButtonEn == true) ||
       (gt_hmiObjct.b_menuButtonEn == true) ||
       (gt_hmiObjct.b_pwrDownButtonEn == true))
   {
      /*---------------------------------------------------------------------*
       * Button pressed, restart counter so that we remain on the home screen.
       *---------------------------------------------------------------------*/
      gt_hmiObjct.t_startTime = arb_sysTimeNow();

   }

//Todo cycle through various menus when the 'b_menuButtonEn' is enabled
//This probably needs to be a linked-list of function ptrs...
c_elapsedTime = usr_mainMemu();

   /*------------------------------------------------------------------------*
    * Is it time to transition to a new state?
    *------------------------------------------------------------------------*/
   if( gt_hmiObjct.b_pwrDown == true)
   {
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      /*---------------------------------------------------------------------*
       * Turn the display off
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_DISPLAY_OFF,
                 0);

      /*---------------------------------------------------------------------*
       * Turn the back light off
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      /*---------------------------------------------------------------------*
       * Tell the BMS to get ready to power the system down...
       *---------------------------------------------------------------------*/
      usr_sendPwrDwnMsgToBms();
   }
   else if( gt_hmiObjct.t_mStatus.t_state == BMS_FAULT)
   {
      t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                     (uint8_t)HMI_FAULT,
                                     0);
   }
   else if( gt_hmiObjct.t_mStatus.t_state == BMS_DEEP_DIST)
   {

      t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                     (uint8_t)HMI_DEEP_DISCHARGE,
                                     0);
   }
   else if( c_elapsedTime >= HMI_HOME_SCREEN_DURATION)
   {
      t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                     (uint8_t)HMI_IDLE,
                                     0);
   }

}/*End usr_hmiHome*/

static void usr_hmiIdle( int32_t i_stateData,
                         uint8_t c_prevState)
{
   t_stmnError t_stErr;

   if( c_prevState != HMI_IDLE)
   {

      gt_hmiObjct.t_prevState = (t_hmiState)c_prevState;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_DISPLAY_OFF,
                 0);

   }/*End if( c_prevState != HMI_IDLE)*/

   /*------------------------------------------------------------------------*
    * Has the user requested power down?
    *------------------------------------------------------------------------*/
   usr_hmiUpdatePwrDwnBtn();

   /*------------------------------------------------------------------------*
    * Is it time to transition to another state?
    *------------------------------------------------------------------------*/
   if( gt_hmiObjct.t_mStatus.c_fault)
   {
      t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                     (uint8_t)HMI_FAULT,
                                     0);
   }
   else if( gt_hmiObjct.t_mStatus.t_state == BMS_DEEP_DIST)
   {

      t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                     (uint8_t)HMI_DEEP_DISCHARGE,
                                     0);
   }
   else if( (gt_hmiObjct.b_bckLghtButtonEn == true) ||
            (gt_hmiObjct.b_menuButtonEn == true) ||
            (gt_hmiObjct.b_pwrDownButtonEn == true))
   {
      /*---------------------------------------------------------------------*
       * Return to the home state
       *---------------------------------------------------------------------*/
      t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                     (uint8_t)HMI_HOME,
                                     0);
   }

}/*End usr_hmiIdle*/

static void usr_hmiFault( int32_t i_stateData,
                          uint8_t c_prevState)
{
   t_cursPos t_cur;
   uint8_t ac_buff[50];
   uint16_t s_size;
   t_stmnError t_stErr;

   if( c_prevState != HMI_FAULT)
   {

      gt_hmiObjct.t_prevState = (t_hmiState)c_prevState;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CLEAR,
                 0);

   }/*End if( c_prevState != HMI_FAULT)*/

   /*------------------------------------------------------------------------*
    * Has the user requested power down?
    *------------------------------------------------------------------------*/
   usr_hmiUpdatePwrDwnBtn();

   if( gt_hmiObjct.t_mStatus.c_fault & BMS_FAULT_TEMP)
   {

      t_cur.c_row = 1;
      t_cur.c_col = 2;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("TEMPERATURE LIMIT!"));
      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 1;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Output turned off"));
      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

   }
   else if( gt_hmiObjct.t_mStatus.c_fault & BMS_FAULT_OVER_CURRENT)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 2;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("CURRENT LIMIT!"));
      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 1;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Output turned off"));
      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      t_cur.c_row = 3;
      t_cur.c_col = 0;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Press btn to reset"));
      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      /*------------------------------------------------------------------------*
       * Has the user acknowledged the over-current condition by pressing a
       * button?
       *------------------------------------------------------------------------*/
      if( (gt_hmiObjct.b_bckLghtButtonEn == true) ||
          (gt_hmiObjct.b_menuButtonEn == true) ||
          (gt_hmiObjct.b_pwrDownButtonEn == true))
      {

         /*---------------------------------------------------------------------*
          * Let the BMS thread know that the condition has been acknowledged.
          *---------------------------------------------------------------------*/
         usr_sendFaultAckToBms();

         /*---------------------------------------------------------------------*
          * Return to the home screen...
          *---------------------------------------------------------------------*/
         t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                        (uint8_t)HMI_HOME,
                                        0);
      }
   }
   else if( gt_hmiObjct.t_mStatus.c_fault & BMS_FAULT_NO_BATT)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 2;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("NO BATT DETECTED!"));
      arb_write( gt_hmiObjct.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 1;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Output turned off"));
      arb_write( gt_hmiObjct.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);
   }
   else if( gt_hmiObjct.t_mStatus.c_fault & BMS_FAULT_CHARGE)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 2;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("CHARGE FAULT!"));
      arb_write( gt_hmiObjct.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 1;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Output turned off"));
      arb_write( gt_hmiObjct.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);
      t_cur.c_row = 3;
      t_cur.c_col = 0;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Press btn to reset"));
      arb_write( gt_hmiObjct.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      /*------------------------------------------------------------------------*
       * Has the user acknowledged the over-current condition by pressing a
       * button?
       *------------------------------------------------------------------------*/
      if( (gt_hmiObjct.b_bckLghtButtonEn == true) ||
          (gt_hmiObjct.b_menuButtonEn == true) ||
          (gt_hmiObjct.b_pwrDownButtonEn == true))
      {

         /*---------------------------------------------------------------------*
          * Let the BMS thread know that the condition has been acknowledged.
          *---------------------------------------------------------------------*/
         usr_sendFaultAckToBms();

         /*---------------------------------------------------------------------*
          * Return to the home screen...
          *---------------------------------------------------------------------*/
         t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                        (uint8_t)HMI_HOME,
                                        0);
      }
   }
   else
   {
     /*---------------------------------------------------------------------*
       * Return to the home screen...
       *---------------------------------------------------------------------*/
      t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                     (uint8_t)HMI_HOME,
                                     0);
   }

   if( gt_hmiObjct.b_pwrDown == true)
   {
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      /*---------------------------------------------------------------------*
       * Turn the display off
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_DISPLAY_OFF,
                 0);

      /*---------------------------------------------------------------------*
       * Turn the back light off
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      /*---------------------------------------------------------------------*
       * Tell the BMS to get ready to power the system down...
       *---------------------------------------------------------------------*/
      usr_sendPwrDwnMsgToBms();
   }

}/*End usr_hmiFault*/

static bool usr_sendPwrDwnMsgToBms( void)
{
   int16_t s_numBytes;
   t_MAILBOXHNDL t_bmsMlbxHndl = usr_getBmsInMlbxHndle();
   t_bmsPwrDownMsg t_pwrDownMsg;

   t_pwrDownMsg.c_msgId = BMS_CMD_PWR_DOWN;
   s_numBytes = arb_mailboxWrite( t_bmsMlbxHndl,
                                  (int8_t *)&t_pwrDownMsg,
                                  (int16_t)sizeof(t_pwrDownMsg));

   /*------------------------------------------------------------------------*
    * Was the message successfully sent?
    *------------------------------------------------------------------------*/
   if( s_numBytes > 0) /*Yes*/
   {
      return true;
   }

   return false;

}/*End usr_sendPwrDwnMsgToBms*/

static bool usr_sendFaultAckToBms( void)
{
   int16_t s_numBytes;
   t_MAILBOXHNDL t_bmsMlbxHndl = usr_getBmsInMlbxHndle();
   t_bmsFaultAckMsg t_faultAck;

   t_faultAck.c_msgId = BMS_CMD_FAULT_ACK;
   s_numBytes = arb_mailboxWrite( t_bmsMlbxHndl,
                                  (int8_t *)&t_faultAck,
                                  (int16_t)sizeof( t_faultAck));

   /*------------------------------------------------------------------------*
    * Was the message successfully sent?
    *------------------------------------------------------------------------*/
   if( s_numBytes > 0) /*Yes*/
   {
      return true;
   }

   return false;

}/*End usr_sendFaultAckToBms*/

static void usr_hmiDeepDischarge( int32_t i_stateData,
                                  uint8_t c_prevState)
{
   t_cursPos t_cur;
   uint8_t ac_buff[50];
   uint16_t s_size;
   t_stmnError t_stErr;
   int8_t c_timeLeft;

   if( c_prevState != HMI_DEEP_DISCHARGE)
   {

      gt_hmiObjct.t_prevState = (t_hmiState)c_prevState;

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_DISPLAY_ON,
                 0);

   }/*End if( c_prevState != HMI_DEEP_DISCHARGE)*/

   /*------------------------------------------------------------------------*
    * Has the user requested power down?
    *------------------------------------------------------------------------*/
   usr_hmiUpdatePwrDwnBtn();

   c_timeLeft = usr_getDeepDisTimeRem();

   t_cur.c_row = 1;
   t_cur.c_col = 0;

   arb_ioctl( gt_hmiObjct.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("Power off in %d sec"), c_timeLeft);
   arb_write( gt_hmiObjct.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);

   /*------------------------------------------------------------------------*
    * Has the BMS left the deep discharge state?
    *------------------------------------------------------------------------*/
   if( gt_hmiObjct.b_pwrDown == true)
   {
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      /*---------------------------------------------------------------------*
       * Turn the display off
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_DISPLAY_OFF,
                 0);

      /*---------------------------------------------------------------------*
       * Turn the back light off
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_hmiObjct.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      /*---------------------------------------------------------------------*
       * Tell the BMS to get ready to power the system down...
       *---------------------------------------------------------------------*/
      usr_sendPwrDwnMsgToBms();

   }
   else if( gt_hmiObjct.t_mStatus.t_state != BMS_DEEP_DIST)
   {

      /*---------------------------------------------------------------------*
       * Return to the home screen...
       *---------------------------------------------------------------------*/
      t_stErr = utl_stMnChangeState( gt_hmiObjct.t_stateMn,
                                     (uint8_t)HMI_HOME,
                                     0);

   }

}/*End usr_hmiDeepDischarge*/

static void usr_hmi( t_parameters t_param,
                     t_arguments  t_args)
{
   t_stmnError t_stErr;

   while( RUN_FOREVER)
   {

      /*---------------------------------------------------------------------*
       * Global data is being accessed, apply lock...
       *---------------------------------------------------------------------*/
      arb_wait( gt_hmiObjct.t_mutex, 0);

      /*---------------------------------------------------------------------*
       * Grab the latest battery voltage, current, temperature, and fuel gage
       * measurements along with the current BMS state.
       *---------------------------------------------------------------------*/
      gt_hmiObjct.t_mStatus = usr_getBmsMeas();

      /*---------------------------------------------------------------------*
       * Has the back light, menu, or power down button been pressed?
       *---------------------------------------------------------------------*/
      usr_checkForButtonPress();

      /*---------------------------------------------------------------------*
       * Transition to the next state...
       *---------------------------------------------------------------------*/
      t_stErr = utl_stMnEngine( gt_hmiObjct.t_stateMn);

      /*---------------------------------------------------------------------*
       * Clear the back light and menu button indicators...
       *---------------------------------------------------------------------*/
      usr_clearButtonPress();

      /*---------------------------------------------------------------------*
       * unlock...
       *---------------------------------------------------------------------*/
      arb_signal( gt_hmiObjct.t_mutex);

      /*---------------------------------------------------------------------*
       * Allow other threads to run...
       *---------------------------------------------------------------------*/
      arb_sleep( HMI_SLEEP_TICKS);

   }/*End while( RUN_FOREVER)*/

}/*End usr_hmi*/

static void usr_hmiStateMachineInit( void)
{
   uint8_t ac_transMap[HMI_NUM_STATES][HMI_NUM_STATES];

   /*------------------------------------------------------------------------*
    * Allocate memory for the HMI state-machine.
    *------------------------------------------------------------------------*/
   gt_hmiObjct.t_stateMn = utl_requestStMnObject( HMI_NUM_STATES,
                                                  HMI_BOOT);
   if( gt_hmiObjct.t_stateMn < 0)
      exit(0);

   /*------------------------------------------------------------------------*
    * Populate the state-machine function table.
    *------------------------------------------------------------------------*/
   if( utl_stMnPopFunMap( gt_hmiObjct.t_stateMn,
                          &usr_hmiBoot,
                          (uint8_t)HMI_BOOT) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_hmiObjct.t_stateMn,
                          &usr_hmiHome,
                          (uint8_t)HMI_HOME) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_hmiObjct.t_stateMn,
                          &usr_hmiIdle,
                          (uint8_t)HMI_IDLE) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_hmiObjct.t_stateMn,
                          &usr_hmiFault,
                          (uint8_t)HMI_FAULT) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_hmiObjct.t_stateMn,
                          &usr_hmiDeepDischarge,
                          (uint8_t)HMI_DEEP_DISCHARGE) < 0)
   {
      exit(0);
   }

   /*------------------------------------------------------------------------*
    * Populate the state-machine state transition table.
    *------------------------------------------------------------------------*/
   memset( (void *)ac_transMap, 0, sizeof( ac_transMap));

   ac_transMap[HMI_BOOT][HMI_BOOT] = 1;
   ac_transMap[HMI_BOOT][HMI_HOME] = 1;

   ac_transMap[HMI_HOME][HMI_HOME]           = 1;
   ac_transMap[HMI_HOME][HMI_IDLE]           = 1;
   ac_transMap[HMI_HOME][HMI_FAULT]          = 1;
   ac_transMap[HMI_HOME][HMI_DEEP_DISCHARGE] = 1;

   ac_transMap[HMI_IDLE][HMI_IDLE]           = 1;
   ac_transMap[HMI_IDLE][HMI_HOME]           = 1;
   ac_transMap[HMI_IDLE][HMI_FAULT]          = 1;
   ac_transMap[HMI_IDLE][HMI_DEEP_DISCHARGE] = 1;

   ac_transMap[HMI_FAULT][HMI_FAULT] = 1;
   ac_transMap[HMI_FAULT][HMI_HOME]  = 1;

   ac_transMap[HMI_DEEP_DISCHARGE][HMI_DEEP_DISCHARGE] = 1;/*State 5->5 valid*/
   ac_transMap[HMI_DEEP_DISCHARGE][HMI_HOME]           = 1;/*State 5->1 valid*/

   utl_stMnPopTransMap( gt_hmiObjct.t_stateMn,
                        &ac_transMap[0][0]);

}/*End usr_hmiStateMachineInit*/

/*---------------------------------------------------------------------------*
 * Public Function Definitions
 *---------------------------------------------------------------------------*/
t_error usr_hmiInit( void)
{
   t_gpioConf t_conf;

   /*------------------------------------------------------------------------*
    * Initialize the HMI object...
    *------------------------------------------------------------------------*/
   memset( (void *)&gt_hmiObjct, 0, sizeof( t_hmiObjct));

   /*------------------------------------------------------------------------*
    * Create a new thread.
    *------------------------------------------------------------------------*/
   gt_hmiObjct.t_hmiThread = arb_threadCreate( usr_hmi,
                                               0,
                                               0,
                                               ARB_STACK_384B,
                                               1);

   if( gt_hmiObjct.t_hmiThread < 0)
   {
      return gt_hmiObjct.t_hmiThread;
   }/*End if( gt_hmiObjct.t_hmiThread < 0)*/

   /*------------------------------------------------------------------------*
    * Open a handle to the BMS driver.
    *------------------------------------------------------------------------*/
   gt_hmiObjct.t_lcdHndl = arb_open( "lcdDevice0",
                                     ARB_O_READ |
                                     ARB_O_WRITE);

   if( gt_hmiObjct.t_lcdHndl < 0)
   {
      return gt_hmiObjct.t_lcdHndl;
   }

   t_conf.c_inputMask  = HMI_BACKLIGHT_PIN | HMI_MENU_PIN | HMI_PWR_PIN;
   t_conf.c_outputMask = 0;
   t_conf.t_inConf     = PULLDOWN;
   t_conf.t_outConf    = TOTEM;

   if( utl_configureGpioPort( HMI_BUTTON_PORT, t_conf) < 0)
   {
      return ARB_UTL_ERROR;
   }

   usr_hmiStateMachineInit();

   return ARB_PASSED;

}/*End usr_hmiInit*/
#endif